require 'test/unit'

require 'models/character'
require 'engine/action'
require 'engine/logic'

class CharacterDatabase
    def add(c)
        @instances.add(c)
    end
end

class TCLogic < Logic
    applicable_to Character
    invoked_by
    def do_logic(action)
        if action.type == :true
            $global += 1
            return true
        end
        return false
    end
end
class TC2Logic < Logic
    applicable_to Character
    invoked_by
    stackable :replaceable
end
class TC3Logic < Logic
    applicable_to Character
    invoked_by
    stackable :stackable
end

class TC4Logic < Logic
    applicable_to Character
    invoked_by :action

    properties :counter

    def do_added
        self.counter+=1
    end
end
class TC5Logic < Logic
    applicable_to Character
    invoked_by :action
    stackable :stackable

    def do_added
        $tc5 +=1
    end
    def do_removed
        return false if $tc5 == 0
        $tc5 -=1
    end
end

class LogicEntityTest < Test::Unit::TestCase
    def setup
        @char = Character.new
        @char.oid = 1
        CharacterDatabase.instance.add(@char)
    end
    def test_simple_logic_entity
        assert(@char.respond_to?(:do_action))
    end
    def test_do_action
        assert(@char.do_action(Action.new(:action,@char)))
    end
    def test_do_action_advanced
        $global = 0

        @char.add_logic(TCLogic.new(@char))

        assert(@char.do_action(Action.new(:true,@char)))
        assert(1,$global)
        @char.do_action(Action.new(:false,@char))
        assert(1,$global)
    end
    def test_add_logic
        @char.add_logic(TCLogic.new(@char))
        # Exclusive Logic
        assert_raises(RuntimeError) {@char.add_logic(TCLogic.new(@char))}

        # Replaceable Logic
        assert_nothing_thrown{@char.add_logic(TC2Logic.new(@char))}
        assert_equal(2,@char.instance_variable_get("@props")[:logic].size)
        assert_nothing_thrown{@char.add_logic(TC2Logic.new(@char))}
        assert_equal(2,@char.instance_variable_get("@props")[:logic].size)

        # Stackable Logic
        assert_nothing_thrown{@char.add_logic(TC3Logic.new(@char))}
        assert_equal(3,@char.instance_variable_get("@props")[:logic].size)
        assert_nothing_thrown{@char.add_logic(TC3Logic.new(@char))}
        assert_equal(4,@char.instance_variable_get("@props")[:logic].size)
    end
    def test_do_added
        t = TC4Logic.new(@char)
        t.counter=1
        @char.add_logic(t)
        assert_equal(2,t.counter)
    end
    def test_01_del_logic
        @char.add_logic(TCLogic.new(@char))

        @char.del_logic(TC2Logic)
        assert(@char.logic.find{|l|l.class==TCLogic})
        assert(! @char.logic.find{|l|l.class==TC2Logic})
    end
    def test_02_del_logic
        @char.add_logic(TC3Logic.new(@char))
        @char.add_logic(TC3Logic.new(@char))

        @char.del_logic(TC3Logic)
        assert_equal(0,@char.logic.size)
    end
    def test_03_del_logic
        bad = Character.new
        bad.oid = 2

        logic = TCLogic.new(@char)
        bad_logic = TCLogic.new(bad)

        assert_raises(RuntimeError) {@char.del_logic(bad_logic)}
    end
    def test_04_del_logic
        $tc5 = 0
        one = TC5Logic.new(@char)
        two = TC5Logic.new(@char)
        @char.add_logic(one)
        @char.add_logic(two)

        assert_equal(2,$tc5)

        @char.del_logic(TC5Logic)
        assert_equal(0,$tc5)

        @char.del_logic(TC5Logic)
        assert_equal(0,$tc5)
    end
end
